use axum::http::{HeaderMap, HeaderName, HeaderValue, Method, Uri};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestLog {
    pub timestamp: DateTime<Utc>,
    #[serde(serialize_with = "serialize_method", deserialize_with = "deserialize_method")]
    pub method: Method,
    #[serde(serialize_with = "serialize_uri", deserialize_with = "deserialize_uri")]
    pub uri: Uri,
    #[serde(serialize_with = "serialize_headers", deserialize_with = "deserialize_headers")]
    pub headers: HeaderMap,
    pub body: String,
    pub remote_addr: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub json_file_path: Option<String>,
}

fn serialize_method<S>(method: &Method, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serializer.serialize_str(method.as_str())
}

fn deserialize_method<'de, D>(deserializer: D) -> Result<Method, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let s = String::deserialize(deserializer)?;
    s.parse().map_err(serde::de::Error::custom)
}

fn serialize_uri<S>(uri: &Uri, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serializer.serialize_str(&uri.to_string())
}

fn deserialize_uri<'de, D>(deserializer: D) -> Result<Uri, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let s = String::deserialize(deserializer)?;
    s.parse().map_err(serde::de::Error::custom)
}

fn serialize_headers<S>(headers: &HeaderMap, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    let map: HashMap<String, String> = headers
        .iter()
        .map(|(name, value)| {
            (
                name.to_string(),
                value.to_str().unwrap_or_default().to_string(),
            )
        })
        .collect();
    map.serialize(serializer)
}

fn deserialize_headers<'de, D>(deserializer: D) -> Result<HeaderMap, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
    let mut headers = HeaderMap::new();
    for (key, value) in map {
        let header_name = HeaderName::from_bytes(key.as_bytes())
            .map_err(serde::de::Error::custom)?;
        let header_value = HeaderValue::from_str(&value)
            .map_err(serde::de::Error::custom)?;
        headers.insert(header_name, header_value);
    }
    Ok(headers)
}

impl RequestLog {
    pub fn new(
        method: Method,
        uri: Uri,
        headers: HeaderMap,
        body: String,
        remote_addr: String,
        json_file_path: Option<String>,
    ) -> Self {
        Self {
            timestamp: Utc::now(),
            method,
            uri,
            headers,
            body,
            remote_addr,
            json_file_path,
        }
    }

    pub fn to_json(&self) -> String {
        serde_json::to_string_pretty(self).unwrap_or_default()
    }
} 